package com.chinackts.common.okhttp;

import com.chinackts.auth.model.ProxyIP;
import com.chinackts.common.property.ProxyApiSelectionProperties;
import org.joda.time.DateTime;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.net.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author Lianhong
 * @since 2021/9/9
 */
@DependsOn({"okHttpRequest"/*, "proxyIPRepository"*/})
@Component
public class OkHttpProxySelector extends ProxySelector {

    private static final Logger LOGGER = LoggerFactory.getLogger(OkHttpProxySelector.class);

//    @Value("${proxy.type}")
//    private ProxyType proxyType;
//
//    @Value("${proxy.url}")
//    private String ipUrl;

//    @Resource
//    private ProxyIPRepository proxyIPRepository;

    @Resource
    private ProxyWeightedSelector proxyWeightedSelector;

    @Resource
    private OkHttpRequest okHttpRequest;

    @Resource
    private RedissonClient redissonClient2;

    private final Object lock = new Object();

    @Override
    public List<Proxy> select(URI uri) {
        ProxyIP proxyData = proxyWeightedSelector.getProxyIP();
        List<Proxy> proxyList = this.convertToProxyList(proxyData);
        if (CollectionUtils.isEmpty(proxyList)) {
//                proxyList = this.convertToProxyList(this.requestProxyIp());
            ProxyApiSelectionProperties selectedApi = proxyWeightedSelector.selectApiByWeight();
            if (selectedApi != null) {
                ProxyIP proxyIP = requestProxyFromApi(selectedApi);
                proxyList = convertToProxyList(proxyIP);
            }
        }
        return proxyList;
    }

    @Override
    public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
        String host = ((InetSocketAddress) sa).getHostString();
        int port = ((InetSocketAddress) sa).getPort();
        LOGGER.error("OkHttpProxySelector -> connectFailed 代理失败：address[{}:{}]，exception[{}]", host, port, ioe.getMessage());
        //删除代理失败的IP
        this.removeInvalidProxyIp(host, port);
    }

    private List<Proxy> convertToProxyList(ProxyIP proxyIP) {
        if (proxyIP == null) return null;
        List<ProxyIP.Item> data = proxyIP.getItems();
        if (CollectionUtils.isEmpty(data)) return null;
        List<Proxy> proxyList = new ArrayList<>();
        data.forEach(item -> {
            String ip = item.getIp();
            int port = item.getPort();
            String expireTime = item.getExpireTime();
            if (this.validateExpireTime(expireTime)) return;
            proxyList.add(new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(ip, port)));
        });
//        LOGGER.info("current proxy ip: {}", data);
        return proxyList;
    }

    private ProxyIP requestProxyFromApi(ProxyApiSelectionProperties api) {
        String url = api.getUrl();
        RLock lock = redissonClient2.getLock("EP-PROXY_API_FETCH_LOCK:" + url);
        try {
            boolean locked = lock.tryLock(3, 30, TimeUnit.SECONDS);
            if (!locked) {
                LOGGER.warn("获取分布式锁失败，放弃调用代理 API：{}", url);
            }
            ProxyIP cachedProxyIp = proxyWeightedSelector.getProxyIP();
            if (cachedProxyIp != null && !CollectionUtils.isEmpty(cachedProxyIp.getItems())) {
                List<Proxy> proxyList = this.convertToProxyList(cachedProxyIp);
                if (proxyList.isEmpty()) {
                    LOGGER.info("其他服务器的代理IP已不可用，获取新的IP");
                } else {
                    LOGGER.info("其他服务器已更新了代理IP，可直接使用缓存数据");
                    return cachedProxyIp;
                }
            }

            String resp = okHttpRequest.get(url, null);
            LOGGER.info("从API[{}]获取代理IP响应: {}", url, resp);
//            ProxyIP proxyIP = ProxyIP.applyFor(resp, api.getType());
            ProxyIP proxyIP = ProxyIP.applyFor(resp, api.getType());

            proxyWeightedSelector.saveProxyIP(proxyIP);
            return proxyIP;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            LOGGER.error("获取分布式锁时被中断：", e);
            return null;
        } catch (Exception e) {
            LOGGER.error("从API[{}]获取代理IP失败:", url, e);
            return null;
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 请求获取代理IP
     */
//    private ProxyIP requestProxyIp() {
//        String resp = okHttpRequest.get(ipUrl, null);
//        LOGGER.info("代理IP响应: {}", resp);
//        ProxyIP proxyIP = ProxyIP.applyFor(resp, proxyType);
//        proxyIPRepository.saveProxyIP(proxyIP);
//        return proxyIP;
//    }

    /**
     * 删除代理失败的IP
     */
    private void removeInvalidProxyIp(String host, Integer port) {
        ProxyIP proxyIP = proxyWeightedSelector.getProxyIP();
        if (proxyIP == null) return;
        proxyIP.getItems().removeIf(item -> host.equals(item.getIp()) && port.equals(item.getPort()));
        proxyWeightedSelector.saveProxyIP(proxyIP);
    }

    /**
     * 到期时间 <= 当前时间 ?
     *
     * @return 到期时间 <= 当前时间
     */
    private boolean validateExpireTime(String expireTime) {
        return expireTime.compareTo(DateTime.now().toString("yyyy-MM-dd HH:mm:ss")) <= 0;
    }
}
